import 'dart:async';
import 'dart:ffi';
import 'dart:math' as Math;
import 'dart:typed_data';

import 'package:dartx/dartx.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:get/get_common/get_reset.dart';
import 'package:jiffy/jiffy.dart';
import 'package:sgy_flutter/ble/base_service.dart';
import 'package:sgy_flutter/ble/ext.dart';
import 'package:sgy_flutter/ble/icustom_service.dart';
import 'package:sgy_flutter/ble/packet.dart';
import 'package:sgy_flutter/ble/sgy_cmd.dart';
import 'package:sgy_flutter/model/abnormal_zero_record.dart';
import 'package:sgy_flutter/model/detection_result_model.dart';
import 'package:sgy_flutter/model/dfu_info_model.dart';
import 'package:sgy_flutter/model/dls_upload_model.dart';
import 'package:sgy_flutter/model/dls_upload_model_old.dart';
import 'package:sgy_flutter/model/ota_info_model.dart';

import '../model/pair_state.dart';
import 'errors.dart';

/**
 * 新版AXO和DLS的透传
 */
class CustomService extends BaseService implements ICustomService {
  static const _serviceUUID = ("6e400001-b5a3-f393-e0a9-e50e24dcaa9e");
  static const _writeUUID = ("6e400002-b5a3-f393-e0a9-e50e24dcaa9e");
  static const _notificationUUID = ("6e400003-b5a3-f393-e0a9-e50e24dcaa9e");

  StreamController<Packet?> _packetStreamController =
      StreamController.broadcast();

  Stream<Packet?> get packetStream =>
      _packetStreamController.stream.where((test) => test != null);


  @override
  Stream<DlsUploadModelOld> get dlsUploadDataOld {
    return _packetStreamController.stream.transform(
        StreamTransformer<Packet?, DlsUploadModelOld>.fromHandlers(
            handleData: (packet, sink) {
      if (packet?.cmd == SgyCMD.oldDlsDataUpload) {

        final byteData =
            Uint8List.fromList(packet?.data ?? []).buffer.asByteData();
        sink.add(DlsUploadModelOld(
            power: byteData.getUint16(0, Endian.little),
            cadence: byteData.getUint16(2, Endian.little),
            cadenceSmooth: byteData.getInt8(4),
            torsionAvailability: byteData.getUint8(5)));
      }
    }));
  }

  Stream<ByteData> get uploadData{
    return _packetStreamController.stream.transform(StreamTransformer<Packet?, ByteData>.fromHandlers(
        handleData: (packet, sink) {
          printInfo(info: "${packet?.cmd}");
          if (packet?.cmd == SgyCMD.dataUpload){
            sink.add(Uint8List.fromList(packet!.data).buffer.asByteData());
          }
        }));
  }

  @override
  Stream<DlsUploadModel> get dlsUploadModel {
    return _packetStreamController.stream.transform(
      StreamTransformer<Packet?, DlsUploadModel>.fromHandlers(
          handleData: (packet, sink) {
        printInfo(
            info:
                "dlsUploadModel:${packet?.cmd},length:${packet?.toByteArray().length},dataLength:${packet?.data.length}");
        if (packet?.cmd == SgyCMD.dataUpload) {
          try {
            final byteData =
                Uint8List.fromList(packet?.data ?? []).buffer.asByteData();
            final model = DlsUploadModel(
              power: byteData.getUint16(0, Endian.little),
              cadence: byteData.getUint16(2, Endian.little),
              leftRightBalance: byteData.getUint8(4),
              leftCadenceSmooth: byteData.getUint8(5),
              rightCadenceSmooth: byteData.getUint8(6),
              leftTorsionAvailability: byteData.getUint8(7),
              rightTorsionAvailability: byteData.getUint8(8),
            );
            sink.add(model);
          } catch (e) {
            printError(info: "dlsUploadModel:${e}");
          }
        }
      }),
    );
  }

  CustomService()
      : super(
            serviceGuid: _serviceUUID,
            characteristicNotifyGuid: _notificationUUID,
            characteristicReadGuid: null,
            characteristicWriteGuid: _writeUUID);


  Future<bool> fixHitch(List<int> data)async{
    final bytes = await _request(cmd: SgyCMD.fixHitch, data: data, transformer: _byteArrayTransformer);
    return bytes.first == 0x01;
  }

  Future<Uint8List> checkHitch()async{
    final bytes = await _request(cmd: SgyCMD.fixHitchResp, transformer: _byteArrayTransformer);
    return bytes;
  }

  /**
   * 重置功率偏移
   */
  @override
  Future<Uint8List> resetPowerOffset() async {
    final data = await _request(
        cmd: SgyCMD.resetRatioFactor,
        data: [0],
        transformer: _byteArrayTransformer);
    return data;
  }

  /**
   * 设置功率偏移
   */
  @override
  Future<bool> setPowerOffset(int value) async {
    final data = await _request(
        cmd: SgyCMD.ratioFactor,
        data: value.bytes(endian: Endian.little, bits: 2),
        transformer: _byteDataTransformer);
    return data.getUint8(0) == 0x01;
  }

  /**
   * 倒链归零
   */
  @override
  Future<bool> getReverseChain2Zero() async {
    final data =
        await _request(cmd: SgyCMD.getDLGL, transformer: _byteDataTransformer);
    return data.getUint8(0) == 0x01;
  }

  /**
   * 倒链归零
   */
  @override
  Future<bool> setReverseChain2Zero(bool enable) async {
    final data = await _request(
        cmd: SgyCMD.setDLGL,
        data: [enable ? 0x01 : 0x00],
        transformer: _byteDataTransformer);
    return data.getUint8(0) == 0x01;
  }

  /**
   * 打开运输模式
   */
  @override
  Future<bool> openTransportMode() async {
    final data = await _request(
        cmd: SgyCMD.openYSMS, data: [0x01], transformer: _byteDataTransformer);
    return data.getUint8(0) == 0x01;
  }

  /**
   * 数据上传开关
   * 只能开，李总说断开就自动关
   * @param enable Boolean
   * DLS 所有
   */
  @override
  Future openDataUpload() async {
    await _request(
        cmd: SgyCMD.dataUploadToggle, transformer: _byteArrayTransformer);
  }

  /**
   * 获取比例因子,也就是功率偏移
   * @return ScaleFactorModel
   */
  @override
  Future<int?> getScaleFactor() async {
    try {
      final data = await _request(
          cmd: SgyCMD.getScaleFactor,
          data: [0],
          transformer: _byteDataTransformer);
      return data.getInt16(0, Endian.little);
    } catch (e) {
      print(e);
      return null;
    }
  }

  /**
   * 零点校准
   */
  @override
  Future<Uint8List> adjustZero() async {
    //2022-01-01 00:00:00.000 开始算时间,分为单位
    int time = (DateTime.timestamp().millisecondsSinceEpoch - 1640966400000) ~/ 1000 ~/ 60;
    // int time = (DateTime.timestamp().millisecondsSinceEpoch ~/ 1000 ~/ 1000);

    final timeByteArray = time.bytes(bits: 3);
    final data = await _request(
        cmd: SgyCMD.zeroAdjust,
        data: timeByteArray,
        transformer: _byteArrayTransformer);
    return data;
  }

  /**
   * 获取零点校准记录
   */
  @override
  Future<List<Pair<int, String>>> fetchAdjustZeroRecord() async {
    final data = await _request(
        cmd: SgyCMD.getZeroAdjustRecord,
        data: [0],
        transformer: _byteArrayTransformer);
    List<Pair<int, String>> list = List.empty(growable: true);
    for (final array in data.chunked(5)) {
      var byteData = Uint8List.fromList(array).buffer.asByteData();
      final zeroPoint = byteData.getUint16(0, Endian.little);
      byteData =
          Uint8List.fromList([...array.sublist(2), 0]).buffer.asByteData();
      final time = byteData.getInt32(0, Endian.little);
      if (time == 0xffffff || time == 0 || time == 0x06040d) {
        list.add(Pair(zeroPoint, "----"));
      } else {
        var dateTime = Jiffy.parseFromMillisecondsSinceEpoch(
            time * 1000 * 60 + 1640966400000);
        var str = dateTime.format(pattern: "yyyy-MM-dd HH:mm");
        if (dateTime.isAfter(Jiffy.now())){
          dateTime = Jiffy.parseFromMillisecondsSinceEpoch(
              time * 1000 * 1000);
          str = dateTime.format(pattern: "yyyy-MM-dd");
        }else{
          str = dateTime.format(pattern: "yyyy-MM-dd HH:mm");
        }
        list.add(Pair(zeroPoint, str));
      }
    }
    return list.reversed.toList();
  }

  /**
   * 是否充电中
   */
  @override
  Future<bool> isCharging() async {
    final data = await _request(
        cmd: SgyCMD.getDeviceState, transformer: _byteDataTransformer);
    return data.getUint8(11) == 0x01;
  }

  /**
   * 获取异常零点记录
   */
  @override
  Future<AbnormalZeroRecord> fetchAbnormalZeroRecord() async {
    final data =
        await _request(cmd: SgyCMD.getYCLD, transformer: _byteDataTransformer);
    var model = AbnormalZeroRecord(
        totalCount: data.getUint8(0),
        dateIndex: data.getUint8(1),
        date: 0,
        data: []);
    final u = Uint8List.fromList([...data.buffer.asUint8List(2, 3), 0]);
    model =
        model.copyWith(date: u.buffer.asByteData().getUint32(0, Endian.little));
    List<String> list = List.empty(growable: true);
    printError(info: "data:${data.buffer.asUint8List().toHex()}");
    for (var i = 5; i < data.lengthInBytes; i++) {
      list.add((data.getUint8(i)).toRadixString(10));
    }

    model = model.copyWith(data: list);
    return model;
  }

  /**
   * 获取回退版本，没有则不能回退
   * @return String 可以回退的版本号,如果空的则不能回退
   */
  @override
  Future<OTAInfoModel> getFallbackVersion() async {
    final data = await _request(
        cmd: SgyCMD.prepareOTA, transformer: _byteDataTransformer);
    OTAInfoModel model = OTAInfoModel();
    if (data.lengthInBytes > 0) {
      model = model.copyWith(isCanOTA: data.getUint8(0) == 0x01);
    }
    if (data.lengthInBytes > 1) {
      model = model.copyWith(isCanFallback: data.getUint8(1) == 0x01);
    }
    if (data.lengthInBytes > 2) {
      model = model.copyWith(
          fallbackVersion: String.fromCharCodes(data.buffer.asUint8List(2)));
    }
    return model;
  }

  @override
  Future<DetectionResultModel> detection() async {
    //储存芯+传感器状态（1byte）
    //电量检测值（2byte）
    //比例数据（2byte）
    //高低温数据（2byte）
    //零点数据（2byte）
    //零点补偿（2byte）
    var result = DetectionResultModel();
    final deviceStateResp = await _request(
        cmd: SgyCMD.getDeviceState, transformer: _byteDataTransformer);
    if (deviceStateResp.lengthInBytes >= 11) {
      result = result.copyWith(sensorState: deviceStateResp.getUint8(0));
      result =
          result.copyWith(battery: deviceStateResp.getUint16(1, Endian.little));
      result =
          result.copyWith(ratio: deviceStateResp.getUint16(3, Endian.little));
      result = result.copyWith(
          temperature: deviceStateResp.getUint16(5, Endian.little));
      result = result.copyWith(
          temperatureHex: deviceStateResp.buffer.asUint8List(5, 2).toHex("-"));
      result = result.copyWith(
          zeroPoint: deviceStateResp.getUint16(7, Endian.little));
      result = result.copyWith(
          zeroOffset: deviceStateResp.getUint16(9, Endian.little));
    }

    //0.5s一次，连续获取10个进行判断，每个数据与均值误差在正负15内，零点正常
    List<int> torqueArray = List.empty(growable: true);
    for (var i = 0; i < 10; i++) {
      final torqueDataResp = await _request(
          cmd: SgyCMD.getTorqueData, transformer: _byteDataTransformer);
      final value = torqueDataResp.getUint16(0, Endian.little);
      torqueArray.add(value);
      await Future.delayed(Duration(milliseconds: 500));
    }
    final average = torqueArray.average();
    // 没有一个大于30,则正常
    final isTorqueNormal =
        torqueArray.firstWhereOrNull((it) => (it - average).abs() >= 30) ==
            null;
    result = result.copyWith(isTorqueNormal: isTorqueNormal);
    result = result.copyWith(torqueList: torqueArray);

    var isSensorNormal = false;
    //传感器类型（1byte）
    //  01 加速度计 02 陀螺仪
    //传感器数据（6byte）
    final sensorDataResp = await _request(
        cmd: SgyCMD.getSensorData, transformer: _byteDataTransformer);
    final type = sensorDataResp.getUint8(0);
    {
      result = result.copyWith(sensorType: type);
      if (sensorDataResp.lengthInBytes == 4) {
        List<int> list = List.empty(growable: true);
        final x = sensorDataResp.getInt8(1);
        final y = sensorDataResp.getInt8(2);
        final z = sensorDataResp.getInt8(3);
        list.add(x);
        list.add(y);
        list.add(z);
        result = result.copyWith(
            sensorData: list,
            sensorDataHex: sensorDataResp.buffer.asUint8List().toHex(":"));

        if (type == 1) {
          final value = Math.sqrt(x * x + y * y + z * z * 1) / 256;
          isSensorNormal = value > 60 && value < 68; // value in 60.0..68.0
        }
        if (type == 2) {
          isSensorNormal = (x).abs() < 20 && (y).abs() < 20 && (z).abs() < 20;
        }
      }
      if (sensorDataResp.lengthInBytes == 7) {
        List<int> list = List.empty(growable: true);
        final x = sensorDataResp.getInt16(1, Endian.little);
        final y = sensorDataResp.getInt16(3, Endian.little);
        final z = sensorDataResp.getInt16(5, Endian.little);
        list.add(x);
        list.add(y);
        list.add(z);
        result = result.copyWith(
            sensorData: list,
            sensorDataHex: sensorDataResp.buffer.asUint8List().toHex(":"));
        if (type == 1) {
          final value = Math.sqrt(x * x + y * y + z * z * 1) / 256;
          isSensorNormal = value > 60 && value < 68;
        }
        if (type == 2) {
          isSensorNormal = (x).abs() < 20 && (y).abs() < 20 && (z).abs() < 20;
        }
      }
    }
    result = result.copyWith(result: isTorqueNormal);
    return result;
  }

  final _byteArrayTransformer =
      StreamTransformer<Packet, Uint8List>.fromHandlers(
          handleData: (data, sink) {
    sink.add(Uint8List.fromList(data.data));
  });

  final _byteDataTransformer = StreamTransformer<Packet, ByteData>.fromHandlers(
      handleData: (data, sink) {
    sink.add(Uint8List.fromList(data.data).buffer.asByteData());
  });

  Future<T> _request<T>(
      {required SgyCMD cmd,
      List<int>? data,
      Duration timeout = const Duration(seconds: 5),
      required StreamTransformer<Packet, T> transformer,
      Function(double progress, int count, int total, int onceCount)?
          onProgress,
      SgyCMD respCMD = SgyCMD.unknow}) async {
    if (writeCharacteristic == null) {
      throw EmptyWriteCharacteristicError("$_writeUUID");
    }
    if (respCMD == SgyCMD.unknow) {
      respCMD = cmd;
    }
    StreamController<T> controller = StreamController();
    final subscription = packetStream
        .transform(StreamTransformer<Packet?, Packet>.fromHandlers(
            handleData: (data, sink) {
          if (data?.cmd == respCMD) {
            sink.add(data!);
          }
        }))
        .transform(transformer)
        .listen((packet) {
          controller.sink.add(packet);
        });
    // await Future.delayed(Duration(milliseconds: 100));
    final totalData = Packet(cmd: cmd, data: data ?? []).toByteArray();

    try {
      await _write(data: totalData, onProgress: onProgress);
      var value = await controller.stream.first.timeout(timeout);
      return value;
    } on TimeoutException catch (e) {
      rethrow;
    } catch (e) {
      rethrow;
    } finally {
      subscription.cancel();
      controller.close();
    }
  }

  Future _writeCmd(
      {required SgyCMD cmd,
      List<int>? data,
      Function(double progress, int count, int total, int onceCount)?
          onProgress}) async {
    final totalData = Packet(cmd: cmd, data: data ?? []).toByteArray();
    await _write(data: totalData, onProgress: onProgress);
    // onProgress?.call(1, count, totalData.length, array.last.length);
  }

  Future _write(
      {required List<int> data,
      Function(double progress, int count, int total, int onceCount)?
          onProgress}) async {
    if (writeCharacteristic == null) {
      throw EmptyWriteCharacteristicError("$_writeUUID");
    }
    final array = data.chunked(20).toList();
    var count = 0;
    for (final item in array) {
      await Future.delayed(Duration(milliseconds: 10));
      await writeCharacteristic?.write(item);
      count += item.length;
      onProgress?.call(count / data.length, count, data.length, item.length);
    }
  }

  StreamSubscription? _subscriptionPacket;

  @override
  Future onInitialized(List<BluetoothService> services) {
    _subscriptionPacket =
        stream.transform(packetStreamTransformer()).listen((value) async {
      _packetStreamController.sink.add(value);
      await handlePacketData(value);
    });
    return super.onInitialized(services);
  }

  @override
  Future onCleanup() {
    _subscriptionPacket?.cancel();
    return super.onCleanup();
  }

  @override
  Future<int> getSensorType() async {
    final data = await _request(
        cmd: SgyCMD.getSensor, transformer: _byteDataTransformer);
    return data.getUint8(0);
  }

  @override
  Future<bool> setSensorType(int type) async {
    final data = await _request(
        cmd: SgyCMD.setSensor, data: [type], transformer: _byteDataTransformer);
    return data.getUint8(0) == 0x01;
  }

  @override
  Future<bool> startPair() async{
    final data = await _request(cmd: SgyCMD.setPairState, data: [0x02], transformer: _byteArrayTransformer);
    return data[0] == 0x02;
  }

  @override
  Future<PairState> getPairState() async{
    final data = await _request(cmd: SgyCMD.setPairState, data: [0x02], transformer: _byteArrayTransformer);
    return PairState.from(data[0]);
  }

  @override
  Future<DfuInfoModel> checkDfu() {
    throw UnimplementedError();
  }


}
